package com.atguigu.tingshu.order.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import com.atguigu.tingshu.account.AccountFeignClient;
import com.atguigu.tingshu.album.AlbumFeignClient;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.rabbit.constant.MqConst;
import com.atguigu.tingshu.common.rabbit.service.RabbitService;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.model.order.OrderDerate;
import com.atguigu.tingshu.model.order.OrderDetail;
import com.atguigu.tingshu.model.order.OrderInfo;
import com.atguigu.tingshu.order.helper.SignHelper;
import com.atguigu.tingshu.order.mapper.OrderDerateMapper;
import com.atguigu.tingshu.order.mapper.OrderDetailMapper;
import com.atguigu.tingshu.order.mapper.OrderInfoMapper;
import com.atguigu.tingshu.order.pattern.factory.TradeOrderStrategyFactory;
import com.atguigu.tingshu.order.pattern.strategy.TradeOrderStrategy;
import com.atguigu.tingshu.order.service.OrderDetailService;
import com.atguigu.tingshu.order.service.OrderInfoService;
import com.atguigu.tingshu.user.client.UserFeignClient;
import com.atguigu.tingshu.vo.account.AccountDeductVo;
import com.atguigu.tingshu.vo.order.OrderDerateVo;
import com.atguigu.tingshu.vo.order.OrderDetailVo;
import com.atguigu.tingshu.vo.order.OrderInfoVo;
import com.atguigu.tingshu.vo.order.TradeVo;
import com.atguigu.tingshu.vo.user.UserPaidRecordVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.atguigu.tingshu.common.constant.SystemConstant.*;

@Slf4j
@Service
@RefreshScope //实现Value注解读取实时刷新
@SuppressWarnings({"all"})
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements OrderInfoService {

    @Autowired
    private OrderInfoMapper orderInfoMapper;

    @Autowired
    private UserFeignClient userFeignClient;

    @Autowired
    private AlbumFeignClient albumFeignClient;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private OrderDetailMapper orderDetailMapper;

    @Autowired
    private OrderDetailService orderDetailService;

    @Autowired
    private OrderDerateMapper orderDerateMapper;

    @Autowired
    private AccountFeignClient accountFeignClient;

    @Autowired
    private TradeOrderStrategyFactory tradeOrderStrategyFactory;


    @Autowired
    private RabbitService rabbitService;


    /**
     * 对购买商品（VIP会员、专辑、声音）封装订单结算页所需要数据
     *
     * @param userId  用户ID
     * @param tradeVo {购买商品类别,商品ID,声音数量}
     * @return
     */
    @Override
    public OrderInfoVo trade(Long userId, TradeVo tradeVo) {
        //1.创建订单VO
        OrderInfoVo orderInfoVo = new OrderInfoVo();
        //2.获取购买商品类别：付款项目类型: 1001-专辑 1002-声音 1003-vip会员
        String itemType = tradeVo.getItemType();
        //3.从工厂获取策略实现类对象
        TradeOrderStrategy tradeOrderStrategy = tradeOrderStrategyFactory.getTradeOrderStrategy(itemType);
        tradeOrderStrategy.handleTrade(tradeVo, orderInfoVo);
        return orderInfoVo;
    }

    /**
     * 对购买商品（VIP会员、专辑、声音）封装订单结算页所需要数据
     *
     * @param userId  用户ID
     * @param tradeVo {购买商品类别,商品ID,声音数量}
     * @return
     */
    /*@Override
    public OrderInfoVo trade(Long userId, TradeVo tradeVo) {
        //1.创建订单VO
        OrderInfoVo orderInfoVo = new OrderInfoVo();

        //获取购买商品类别：付款项目类型: 1001-专辑 1002-声音 1003-vip会员
        String itemType = tradeVo.getItemType();
        //1.1.初始化订单结算页三个价格：原价、减免价、订单价
        BigDecimal originalAmount = new BigDecimal("0.00");
        BigDecimal derateAmount = new BigDecimal("0.00");
        BigDecimal orderAmount = new BigDecimal("0.00");
        //1.2.初始化订单结算页两个集合：商品明细集合、商品优惠集合
        List<OrderDetailVo> orderDetailVoList = new ArrayList<>();
        List<OrderDerateVo> orderDerateVoList = new ArrayList<>();

        //2.处理购买商品类别：VIP套餐
        if (ORDER_ITEM_TYPE_VIP.equals(itemType)) {
            //2.1 远程调用"用户服务"获取套餐详情
            VipServiceConfig vipServiceConfig = userFeignClient.getVipServiceConfig(tradeVo.getItemId()).getData();
            Assert.notNull(vipServiceConfig, "套餐:{}不存在", tradeVo.getItemId());
            //2.2 计算订单相关价格 订单价=原价-减免价
            originalAmount = vipServiceConfig.getPrice();
            orderAmount = vipServiceConfig.getDiscountPrice();
            derateAmount = originalAmount.subtract(orderAmount);

            //2.3 封装订单明细列表
            OrderDetailVo orderDetailVo = new OrderDetailVo();
            orderDetailVo.setItemId(tradeVo.getItemId());
            orderDetailVo.setItemName("VIP套餐：" + vipServiceConfig.getName());
            orderDetailVo.setItemUrl(vipServiceConfig.getImageUrl());
            orderDetailVo.setItemPrice(originalAmount);
            orderDetailVoList.add(orderDetailVo);

            //2.4 封装订单优惠列表
            if (originalAmount.compareTo(orderAmount) == 1) {
                OrderDerateVo orderDerateVo = new OrderDerateVo();
                //订单减免类型 1405-专辑折扣 1406-VIP服务折
                orderDerateVo.setDerateType(ORDER_DERATE_VIP_SERVICE_DISCOUNT);
                orderDerateVo.setDerateAmount(derateAmount);
                orderDerateVo.setRemarks("套餐限时减免：" + derateAmount);
                orderDerateVoList.add(orderDerateVo);
            }
        } else if (ORDER_ITEM_TYPE_ALBUM.equals(itemType)) {
            //3. 处理购买商品类别：专辑
            //3.1 远程调用"用户服务"判断是否重复购买专辑 如果是：业务终止
            Long albumId = tradeVo.getItemId();
            Boolean isPaid = userFeignClient.isPaidAlbum(albumId).getData();
            Assert.state(!isPaid, "用户已购买专辑：{}", albumId);

            //3.2 远程调用"专辑服务"获取专辑信息
            AlbumInfo albumInfo = albumFeignClient.getAlbumInfo(albumId).getData();
            Assert.notNull(albumInfo, "专辑:{}不存在", albumId);
            //3.2.1 专辑价格
            BigDecimal price = albumInfo.getPrice();
            //3.2.2 普通用户折扣
            BigDecimal discount = albumInfo.getDiscount();
            //3.2.3 VIP用户折扣
            BigDecimal vipDiscount = albumInfo.getVipDiscount();

            //3.3 远程调用"用户服务"判断用户身份
            Boolean isVIP = false;
            UserInfoVo userInfoVo = userFeignClient.getUserInfoVo(userId).getData();
            Assert.notNull(userInfoVo, "用户:{}不存在", userId);
            if (userInfoVo.getIsVip().intValue() == 1 && userInfoVo.getVipExpireTime().after(new Date())) {
                isVIP = true;
            }
            //3.4 计算相关价格
            //3.4.1 暂时让订单价等于原价
            originalAmount = price;
            orderAmount = originalAmount;
            //3.4.2 如果是普通用户且专辑设置普通用户折扣，则订单价=原价*普通用户折扣
            if (!isVIP && discount.doubleValue() != -1) {
                orderAmount =
                        originalAmount.multiply(discount).divide(new BigDecimal("10"), 2, RoundingMode.HALF_UP);
                derateAmount = originalAmount.subtract(orderAmount);
            }
            //3.4.3 如果是VIP用户且专辑设置VIP用户折扣，则订单价=原价*VIP用户折扣
            if (isVIP && vipDiscount.doubleValue() != -1) {
                orderAmount =
                        originalAmount.multiply(vipDiscount).divide(new BigDecimal("10"), 2, RoundingMode.HALF_UP);
                derateAmount = originalAmount.subtract(orderAmount);
            }
            //3.5 封装订单明细列表、优惠列表
            OrderDetailVo orderDetailVo = new OrderDetailVo();
            orderDetailVo.setItemId(albumId);
            orderDetailVo.setItemName("专辑：" + albumInfo.getAlbumTitle());
            orderDetailVo.setItemUrl(albumInfo.getCoverUrl());
            orderDetailVo.setItemPrice(originalAmount);
            orderDetailVoList.add(orderDetailVo);

            //3.6 封装订单优惠列表
            if (originalAmount.compareTo(orderAmount) == 1) {
                OrderDerateVo orderDerateVo = new OrderDerateVo();
                orderDerateVo.setDerateType(ORDER_DERATE_ALBUM_DISCOUNT);
                orderDerateVo.setDerateAmount(derateAmount);
                orderDerateVo.setRemarks("专辑限时减免：" + derateAmount);
                orderDerateVoList.add(orderDerateVo);
            }
        } else if (ORDER_ITEM_TYPE_TRACK.equals(itemType)) {
            //4.处理购买商品类别：声音
            //4.1 远程调用"专辑服务"获取待结算声音列表
            Long trackId = tradeVo.getItemId();
            List<TrackInfo> waitBuyTrackList = albumFeignClient.getWaitBuyTrackList(trackId, tradeVo.getTrackCount()).getData();
            Assert.notNull(waitBuyTrackList, "不存在待结算商品");

            //4.2 远程调用"专辑服务"查询专辑信息，得到声音单价
            Long albumId = waitBuyTrackList.get(0).getAlbumId();
            AlbumInfo albumInfo = albumFeignClient.getAlbumInfo(albumId).getData();
            Assert.notNull(albumInfo, "声音所属专辑:{}不存在", albumId);
            BigDecimal price = albumInfo.getPrice();

            //4.3 计算相关价格 声音不支持折扣
            originalAmount = price.multiply(BigDecimal.valueOf(tradeVo.getTrackCount()));
            orderAmount = originalAmount;

            //4.4 封装订单明细列表
            orderDetailVoList = waitBuyTrackList
                    .stream()
                    .map(trackInfo -> {
                        OrderDetailVo orderDetailVo = new OrderDetailVo();
                        orderDetailVo.setItemId(trackInfo.getId());
                        orderDetailVo.setItemName("声音：" + trackInfo.getTrackTitle());
                        orderDetailVo.setItemUrl(trackInfo.getCoverUrl());
                        orderDetailVo.setItemPrice(price);
                        return orderDetailVo;
                    }).collect(Collectors.toList());
        }

        //5.封装订单VO 包含三类数据：价格相关、商品相关、其他杂项
        //5.1 为订单价、原价、减免价赋值
        orderInfoVo.setOriginalAmount(originalAmount);
        orderInfoVo.setOrderAmount(orderAmount);
        orderInfoVo.setDerateAmount(derateAmount);
        //5.2 为商品列表、优惠列表赋值
        orderInfoVo.setOrderDetailVoList(orderDetailVoList);
        orderInfoVo.setOrderDerateVoList(orderDerateVoList);

        //5.3 为订单VO其他杂项赋值：购买商品类别、流水号、时间戳及签名
        orderInfoVo.setItemType(itemType);
        //5.3.1 为当前用户本次订单生成流水号，将流水号存入Redis，有效时间5分钟，解决订单重复提交问题
        String tradeNo = IdUtil.randomUUID();
        String tradeKey = RedisConstant.ORDER_TRADE_NO_PREFIX + userId;
        redisTemplate.opsForValue().set(tradeKey, tradeNo, RedisConstant.ORDER_TRADE_EXPIRE, TimeUnit.MINUTES);
        orderInfoVo.setTradeNo(tradeNo);

        //5.3.2 为本次订单生产时间戳及签名
        orderInfoVo.setTimestamp(System.currentTimeMillis());
        //将订单VO转为Map TODO：VO中包含付款方式"payWay"此时为空 生成签名将排除掉payWay字段，故暂时不参与签名
        Map<String, Object> map = BeanUtil.beanToMap(orderInfoVo, false, true);
        String sign = SignHelper.getSign(map);
        orderInfoVo.setSign(sign);
        //6.响应订单VO
        return orderInfoVo;
    }*/

    @Value("${order.cancel}")
    private Integer cancelTTl;

    /**
     * 提交订单
     *
     * @param orderInfoVo
     * @return {orderNo:订单编号}
     */
    @Override
    @GlobalTransactional(rollbackFor = Exception.class)
    public String submitOrder(Long userId, OrderInfoVo orderInfoVo) {
        //1.业务校验-验证流水号 防止回退造成订单重复提交
        //1.1 从Redis中获取当前用户存入流水号值
        String tradeKey = RedisConstant.ORDER_TRADE_NO_PREFIX + userId;
        //1.2 跟前端提交订单VO中流水号比较 采用LUA脚本确保判断跟删除原子性
        String script = "if redis.call(\"get\",KEYS[1]) == ARGV[1]\n" +
                "then\n" +
                "    return redis.call(\"del\",KEYS[1])\n" +
                "else\n" +
                "    return 0\n" +
                "end";
        DefaultRedisScript<Boolean> redisScript = new DefaultRedisScript<>(script, Boolean.class);
        //1.2.1 如果流水号值一致则删除流水号
        Boolean flag = (Boolean) redisTemplate.execute(redisScript, Arrays.asList(tradeKey), orderInfoVo.getTradeNo());
        if (!flag) {
            //1.2.2 反之（超时）抛出异常业务终止
            throw new GuiguException(500, "流水号验证失败");
        }

        //2.业务校验-验证签名 防止用户篡改订单确认页订单相关信息
        //2.1 将订单VO转为Map
        Map<String, Object> map = BeanUtil.beanToMap(orderInfoVo);
        //2.2 跟结算页加签内容一致 排除payWay字段
        map.remove("payWay");
        //2.3 调用验签方法
        SignHelper.checkSign(map);

        //业务校验通过
        //3. 保存订单及订单明细跟优惠明细列表
        OrderInfo orderInfo = this.saveOrderInfo(orderInfoVo);


        String payWay = orderInfoVo.getPayWay();
        //4. 处理付款方式为：余额支付，立即进行扣减余额；虚拟物品发货
        if (ORDER_PAY_ACCOUNT.equals(payWay)) {
            //4.1 远程调用"账户服务"扣减余额
            //4.1.1 构建扣减余额VO对象
            AccountDeductVo accountDeductVo = new AccountDeductVo();
            accountDeductVo.setOrderNo(orderInfo.getOrderNo());
            accountDeductVo.setUserId(orderInfo.getUserId());
            accountDeductVo.setAmount(orderInfo.getOrderAmount());
            accountDeductVo.setContent(orderInfo.getOrderTitle());
            //4.1.2 远程调用完成扣减余额
            Result result = accountFeignClient.checkAndDeduct(accountDeductVo);
            //4.1.3 判断业务状态码是否为200 TODO 如果这里不判断响应业务状态码 下游系统调用发生异常，由于存在全局异常处理，正常响应Result Http请求状态码200
            if (!result.getCode().equals(200)) {
                throw new GuiguException(result.getCode(), result.getMessage());
            }
            //4.1.4 如果扣减余额成功，则保存订单状态为已支付
            orderInfo.setOrderStatus(ORDER_STATUS_PAID);
            baseMapper.updateById(orderInfo);

            //4.2 TODO 远程调用"用户服务"虚拟物品发货
            //4.2.1 构建虚拟物品发货VO对象
            UserPaidRecordVo userPaidRecordVo = new UserPaidRecordVo();
            userPaidRecordVo.setOrderNo(orderInfo.getOrderNo());
            userPaidRecordVo.setUserId(orderInfo.getUserId());
            userPaidRecordVo.setItemType(orderInfo.getItemType());
            List<OrderDetailVo> orderDetailVoList = orderInfoVo.getOrderDetailVoList();
            if (CollUtil.isNotEmpty(orderDetailVoList)) {
                List<Long> itemIdList = orderDetailVoList.stream().map(OrderDetailVo::getItemId)
                        .collect(Collectors.toList());
                userPaidRecordVo.setItemIdList(itemIdList);
                //4.2.2 远程调用"用户服务"虚拟物品发货
                result = userFeignClient.savePaidRecord(userPaidRecordVo);
                //4.2.3 判断远程调用相应结果业务状态码是否为200
                if (!result.getCode().equals(200)) {
                    throw new GuiguException(result.getCode(), result.getMessage());
                }
            }
        }
        if (ORDER_PAY_WAY_WEIXIN.equals(payWay)) {
            //5. TODO 处理付款方式为：微信支付 采用延迟消息进行延迟关单
            //rabbitService.sendDelayMessage(MqConst.EXCHANGE_CANCEL_ORDER, MqConst.ROUTING_CANCEL_ORDER, orderInfo.getId(), MqConst.CANCEL_ORDER_DELAY_TIME);
            rabbitService.sendDelayMessage(MqConst.EXCHANGE_CANCEL_ORDER, MqConst.ROUTING_CANCEL_ORDER, orderInfo.getId(), cancelTTl);
        }

        return orderInfo.getOrderNo();
    }


    /**
     * 保存订单及相关信息
     *
     * @param orderInfoVo
     * @return
     */
    @Override

    public OrderInfo saveOrderInfo(OrderInfoVo orderInfoVo) {
        //1.保存订单信息
        //1.1 将订单VO拷贝到订单PO对象中
        OrderInfo orderInfo = BeanUtil.copyProperties(orderInfoVo, OrderInfo.class);
        //1.2 手动设置订单中属性
        //1.2.1 设置用户ID
        orderInfo.setUserId(AuthContextHolder.getUserId());
        //1.2.2 设置订单状态:未支付 订单状态：0901-未支付 0902-已支付 0903-已取消"
        orderInfo.setOrderStatus(ORDER_STATUS_UNPAID);
        //1.2.3 设置订单购买内容
        List<OrderDetailVo> orderDetailVoList = orderInfoVo.getOrderDetailVoList();
        if (CollUtil.isNotEmpty(orderDetailVoList)) {
            //String orderTitle = orderDetailVoList.stream()
            //        .map(OrderDetailVo::getItemName)
            //        .collect(Collectors.joining(","));
            orderInfo.setOrderTitle(orderDetailVoList.get(0).getItemName());
        }
        //1.2.4 设置订单编号 普通订单编号规则=当日日期+分布式ID生成方案（雪花算法）
        String orderNo = DateUtil.today().replaceAll("-", "") + IdWorker.getIdStr();
        orderInfo.setOrderNo(orderNo);
        //1.3 保存订单，得到订单ID
        baseMapper.insert(orderInfo);
        Long orderId = orderInfo.getId();

        //2.保存订单商品明细列表
        if (CollUtil.isNotEmpty(orderDetailVoList)) {
            //2.1 方式一：调用持久层逐个保存
            //    orderDetailVoList.stream()
            //            .forEach(orderDetailVo -> {
            //                OrderDetail orderDetail = BeanUtil.copyProperties(orderDetailVo, OrderDetail.class);
            //                orderDetail.setOrderId(orderId);
            //                orderDetailMapper.insert(orderDetail);
            //            });
            //2.1 方式二：调用业务层批量保存
            List<OrderDetail> orderDetailList = orderDetailVoList.stream().map(orderDetailVo -> {
                OrderDetail orderDetail = BeanUtil.copyProperties(orderDetailVo, OrderDetail.class);
                orderDetail.setOrderId(orderId);
                return orderDetail;
            }).collect(Collectors.toList());
            orderDetailService.saveBatch(orderDetailList);
        }
        //3.保存订单优惠明细列表
        List<OrderDerateVo> orderDerateVoList = orderInfoVo.getOrderDerateVoList();
        if (CollUtil.isNotEmpty(orderDerateVoList)) {
            orderDerateVoList
                    .stream()
                    .forEach(orderDerateVo -> {
                        OrderDerate orderDerate = BeanUtil.copyProperties(orderDerateVo, OrderDerate.class);
                        orderDerate.setOrderId(orderId);
                        orderDerateMapper.insert(orderDerate);
                    });
        }
        return orderInfo;
    }

    /**
     * 根据订单编号查询订单信息(包含订单明细，优惠列表)
     *
     * @param orderNo
     * @return
     */
    @Override
    public OrderInfo getOrderInfo(String orderNo) {
        //1.根据订单编号查询订单信息
        OrderInfo orderInfo = baseMapper.selectOne(
                new LambdaQueryWrapper<OrderInfo>()
                        .eq(OrderInfo::getOrderNo, orderNo)
        );
        Assert.notNull(orderInfo, "订单:{}不存在", orderNo);
        //2.根据订单ID查询订单明细列表
        List<OrderDetail> orderDetailList = orderDetailMapper.selectList(
                new LambdaQueryWrapper<OrderDetail>()
                        .eq(OrderDetail::getOrderId, orderInfo.getId())
        );
        if (CollUtil.isNotEmpty(orderDetailList)) {
            orderInfo.setOrderDetailList(orderDetailList);
        }


        //3.根据订单ID查询订单优惠列表
        List<OrderDerate> orderDerateList = orderDerateMapper.selectList(
                new LambdaQueryWrapper<OrderDerate>()
                        .eq(OrderDerate::getOrderId, orderInfo.getId())
        );
        if (CollUtil.isNotEmpty(orderDerateList)) {
            orderInfo.setOrderDerateList(orderDerateList);
        }
        return orderInfo;
    }

    /**
     * 查询指定用户订单列表
     *
     * @param pageInfo
     * @param userId
     * @return
     */
    @Override
    public Page<OrderInfo> findUserOrderPage(Page<OrderInfo> pageInfo, Long userId) {
        //1.方式1：采用queryWrapper分页查询订单，遍历订单在查询订单明细
        //2.方式2：采用自定义SQL分页查询订单，同时查询订单明细
        //pageInfo = baseMapper.findUserOrderPage(pageInfo, userId);
        //3.方式3：采用queryWrapper分页查询订单，获取订单ID列表根据订单ID列表查询订单详情，业务逻辑中处理订单明细
        //3.1 分页查询订单
        LambdaQueryWrapper<OrderInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(OrderInfo::getUserId, userId);
        queryWrapper.orderByDesc(OrderInfo::getId);
        pageInfo = this.page(pageInfo, queryWrapper);

        //3.2 获取订单ID列表
        List<OrderInfo> records = pageInfo.getRecords();
        if (CollUtil.isNotEmpty(records)) {
            List<Long> orderIdList = records.stream().map(OrderInfo::getId).collect(Collectors.toList());
            List<OrderDetail> orderDetailList = orderDetailMapper.selectList(
                    new LambdaQueryWrapper<OrderDetail>()
                            .in(OrderDetail::getOrderId, orderIdList)
            );
            //3.3 采用Stream对查询到订单明细列表按照订单ID进行分组 Map中Key为订单ID Map中值为订单明细列表
            Map<Long, List<OrderDetail>> collect = orderDetailList.stream().collect(Collectors.groupingBy(OrderDetail::getOrderId));
            //4.遍历订单找出订单下包含订单明细
            records.stream().forEach(orderInfo -> {
                orderInfo.setOrderDetailList(collect.get(orderInfo.getId()));
            });
        }
        return pageInfo;
    }

    /**
     * 根据订单ID检查订单支付状态，如果未支付则更新订单状态为已取消
     *
     * @param orderId
     */
    @Override
    public void cancelOrder(Long orderId) {
        //1.根据订单ID查询订单信息
        OrderInfo orderInfo = baseMapper.selectById(orderId);
        //2.判断订单状态：如果未支付则更新订单状态为已取消
        if (orderInfo != null && ORDER_STATUS_UNPAID.equals(orderInfo.getOrderStatus())) {
            orderInfo.setOrderStatus(ORDER_STATUS_CANCEL);
            baseMapper.updateById(orderInfo);
            //TODO 远程调用支付服务，修改本地交易记录状态：已关闭
        }
    }

    /**
     * 用户付款成功后：更新订单状态以及虚拟物品发货
     * 面试：毫厘之间，用户最后一刹那付款。订单延迟关单将订单关闭。此时数据不一致
     *
     * @param orderNo
     * @return
     */
    @Override
    public void orderPaySuccess(String orderNo) {
        //1.采用乐观锁方式更新订单状态：更细条件：订单编号+订单状态
        int count = baseMapper.updateOrderStatus(orderNo, ORDER_STATUS_UNPAID, ORDER_STATUS_PAID);
        if (count == 0) {
            //2.TODO 如果更新失败说明订单已经被关闭,远程调用支付系统：主动查询支付状态，如果是已支付，给用户发起退款
            throw new GuiguException(500, "更新订单为已支付失败:" + orderNo);
        }

        //2.远程调用“用户服务”完成虚拟物品发货
        OrderInfo orderInfo = this.getOrderInfo(orderNo);
        //2.1 构建虚拟物品发货VO对象
        UserPaidRecordVo userPaidRecordVo = new UserPaidRecordVo();
        userPaidRecordVo.setOrderNo(orderNo);
        userPaidRecordVo.setUserId(orderInfo.getUserId());
        userPaidRecordVo.setItemType(orderInfo.getItemType());
        List<OrderDetail> orderDetailList = orderInfo.getOrderDetailList();
        if (CollUtil.isNotEmpty(orderDetailList)) {
            List<Long> collect = orderDetailList.stream().map(OrderDetail::getItemId).collect(Collectors.toList());
            userPaidRecordVo.setItemIdList(collect);
            //2.2 远程调用用户服务，完成虚拟物品发货
            Result result = userFeignClient.savePaidRecord(userPaidRecordVo);
            //2.3 判断响应业务状态码
            if(!result.getCode().equals(200)){
                throw new GuiguException(500, "虚拟物品发货失败:" + orderNo);
            }
        }
    }
}
